package com.smashcrush.client.gwt.callback;

import java.util.HashMap;
import java.util.Map;

import com.google.gwt.gen2.logging.shared.Log;
import com.google.gwt.user.client.rpc.AsyncCallback;

/**
 * Manager to handle multiple RPC callbacks
 * 
 * @author jtoth
 */
public abstract class MultiCallbackManager<T> {

	int callbacks;

	Map<String, Throwable> throwables = new HashMap<String, Throwable>();

	Map<String, Object> data = new HashMap<String, Object>();

	public MultiCallbackManager() {
		this(0);
	}
	
	public MultiCallbackManager(int callbacks) {
		this.callbacks = callbacks;
	}
	
	public AsyncCallback generateCallback(final String key) {

		AsyncCallback callback = new AsyncCallback<T>() {

			public void onFailure(Throwable caught) {
				throwables.put(key, caught);
				Log.warning("callback failed: " + key, "cat", caught);
				complete();
			}

			public void onSuccess(T result) {
				data.put(key, result);
				Log.fine("callback successful: " + key);
				complete();
			}
		};

		return callback;
	}

	/**
	 * leaving this public because sometimes we know that one call is
	 * unnecessary, won't return, or was never made... so we can do it manually
	 **/
	public int decrement() {
		return --callbacks;
	}

	private void complete() {
		if (decrement() <= 0) {
			Log.fine("All callbacks returned...");
			if (!throwables.isEmpty()) {
				Log.warning("callback errors detected...");
				handleExceptions();
			} else {
				Log.fine("no callback errors detected...");
				onSuccess();
			}

		}
	}

	private void handleExceptions() {

		// Test if any are access denied
		// for(Throwable throwable : throwables.values()) {
		// if (throwable instanceof AccessDeniedException) {
		// onAccessDeniedExceptionFailure((AccessDeniedException) throwable);
		// return;
		// }
		// }

		// // Aggregate and throw validation exceptions
		// ValidationException validationException = new ValidationException(new
		// ValidationMessages()); //default constructor creates null validation
		// msgs, leads to NPEs
		// for(Throwable throwable : throwables.values()) {
		//
		// if (throwable instanceof ValidationException) {
		// ValidationException e = (ValidationException) throwable;
		// ValidationMessages validationMessages = e.getValidationMessages();
		// if (validationMessages == null) {
		// validationMessages = new ValidationMessages();
		// validationMessages.add("Unknown validation error",
		// "Unknown validation error");
		// }
		// validationException.getValidationMessages().add(validationMessages);
		// }
		// }
		//
		// if (validationException.getValidationMessages().getMessageCount() >
		// 0) {
		// onValidationExceptionFailure(validationException);
		// return;
		// }

		// If not any of the the previous exceptions, just handle all exceptions
		// as Unexpected
		onUnexpectedExceptionFailure();
	}

	public Map<String, Object> getData() {
		return data;
	}

	public Map<String, Throwable> getExceptions() {
		return throwables;
	}

	protected void logAllThrowables() {
		for (Map.Entry<String, Throwable> stringThrowableEntry : throwables
				.entrySet()) {
			if (stringThrowableEntry.getValue() != null) {
				Log
						.fine("MultiCallbackManager received unexpected exception on call: "
								+ stringThrowableEntry.getKey()
								+ " - "
								+ stringThrowableEntry.getValue());
			}
		}
	}

	abstract protected void onSuccess();

//	abstract protected void onValidationExceptionFailure(
//			ValidationException exception);

	// abstract protected void
	// onAccessDeniedExceptionFailure(AccessDeniedException exception);

	abstract protected void onUnexpectedExceptionFailure();

}